package com.apobates.jforum.grief.schema2doc.jdbc;

import com.apobates.jforum.grief.schema2doc.SchemaExportException;
import com.apobates.jforum.grief.schema2doc.core.*;
import com.apobates.jforum.grief.schema2doc.core.entity.Column;
import com.apobates.jforum.grief.schema2doc.core.entity.Information;
import com.apobates.jforum.grief.schema2doc.core.entity.Table;
import com.apobates.jforum.grief.schema2doc.jdbc.out.ConsoleSchemaOutput;
import com.apobates.jforum.grief.schema2doc.output.freeMarker.ExportTemplate;
import com.apobates.jforum.grief.schema2doc.output.freeMarker.SupportEmbedTemplate;

import java.sql.SQLException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 导出执行器
 */
public class ExportExecutor {
    // 输出
    private final SchemaOutput output;
    // 待输出结果集
    private final SchemaQueryResult result;
    // 结果集的查询策略
    private final SchemaTableStrategy strategy;
    // 导出样式模板
    private final ExportTemplate template;

    // 操作的数据库名称
    // Oracle是没有这个概念的
    public final Optional<String> dbName;
    // 模式名称/Oracle和PostgreSQL
    // MySQL和SQLServer是没有这个概念的
    public final Optional<String> schema;

    private ExportExecutor(
            SchemaOutput output,
            SchemaQueryResult result,
            SchemaTableStrategy strategy,
            ExportTemplate template,
            Optional<String> dbName,
            Optional<String> schema) {
        this.output = output;
        this.result = result;
        this.strategy = strategy;
        this.template = template;
        this.dbName = dbName;
        this.schema = schema;
    }

    /**
     * 执行导出
     * @param info 导出信息
     * @return
     * @throws SchemaExportException
     */
    public int export(Information info) throws SchemaExportException {
        long startStamp = System.currentTimeMillis();
        long finishStamp = 0;
        try {
            Stream<Table> tables = result.getAll(dbName, schema, strategy).map(table->{
                try {
                    List<Column> tableColumn = result.getTableColumn(table).collect(Collectors.toList());
                    return table.fillColumn(tableColumn);
                }catch (SQLException e){
                    return null;
                }
            }).filter(Objects::nonNull).map(table -> formattr(table, template));
            if(output instanceof SupportEmbedTemplate set ){
                int affect = set.embed(template).flush(info, tables);
                finishStamp = System.currentTimeMillis();
                System.out.println(String.format("[%s]Export finish. Affect Size: %d WithTime: %d", output.getClass().getSimpleName(), affect, (finishStamp - startStamp)));
                return affect;
            } else {
                int affect = output.flush(info, tables);
                finishStamp = System.currentTimeMillis();
                System.out.println(String.format("[%s]Export finish. Affect Size: %d WithTime: %d", output.getClass().getSimpleName(), affect, (finishStamp - startStamp)));
                return affect;
            }
        }catch (Exception e){
            throw new SchemaExportException(e);
        }
    }

    // 针对值的格式化
    private static Table formattr(Table table, ExportTemplate template) {
        // 长度和精度同时出现
        if(List.of(template.outputColumn()).containsAll(List.of("columnSize", "decimalDigits"))){
            List<Column> collect = table.getColumns().stream().map(column -> column.remold(getBefore(column.getColumnSize(), ','))).collect(Collectors.toList());
            return table.fillColumn(collect);
        }
        return table;
    }

    private static String getBefore(String value, char delimiter){
        if(isNotEmpty(value)) {
            int delimiterIndex = value.indexOf(delimiter);
            if (delimiterIndex != -1) {
                return value.substring(0 , delimiterIndex);  // "name"
            }
        }
        return value;
    }

    private static boolean isNotEmpty(String value){
        return null != value && !value.isEmpty();
    }

    /**
     * 创建导出设置向导
     * @param result
     * @return
     */
    public static Builder supply(SchemaQueryResult result){
        return new Builder(result);
    }


    public static class Builder{
        // 输出
        private SchemaOutput output;
        // 待输出结果集
        private final SchemaQueryResult result;
        // 结果集的查询策略
        private SchemaTableStrategy strategy;
        // 内容模板
        private ExportTemplate template;

        private Builder(SchemaQueryResult result) {
            this.result = result;
            this.strategy = SchemaTableStrategy.empty();
            this.output = new ConsoleSchemaOutput();
            this.template = ExportTemplate.basic();
        }

        /**
         * 设置导出目标
         * @param output
         * @return
         */
        public Builder target(SchemaOutput output){
            this.output = output;
            return this;
        }

        /**
         * 设置导出表的筛选策略
         * @param strategy
         * @return
         */
        public Builder strategy(SchemaTableStrategy strategy){
            this.strategy = strategy;
            return this;
        }

        /**
         * 设置导出的模板.
         * @param template
         * @return
         */
        public Builder template(ExportTemplate template){
            this.template = template;
            return this;
        }
        /**
         * 使用指定数据库创建导出执行器
         * MySQL/SQLServer: 数据库名称
         * @param dbName
         * @return
         */
        public ExportExecutor database(String dbName){
            // SchemaOutput output, SchemaQueryResult result, SchemaTableStrategy strategy, String dbName
            return new ExportExecutor(output, result, strategy, template, Optional.ofNullable(dbName), Optional.empty());
        }

        /**
         * 使用指定数据库和模式创建导出执行器
         * PostgreSQL
         * @param dbName 数据库名称
         * @param schema 模式名称
         * @return
         */
        public ExportExecutor database(String dbName, String schema){
            return new ExportExecutor(output, result, strategy, template, Optional.ofNullable(dbName), Optional.ofNullable(schema));
        }

        /**
         * 使用指定模式创建导出执行器
         * Oracle
         * @param schema
         * @return
         */
        public ExportExecutor schema(String schema){
            return new ExportExecutor(output, result, strategy, template, Optional.empty(), Optional.ofNullable(schema));
        }
    }
}
